/***************************************************************************************** 
*                                     TinyRtos51 
*                                The Real-Time Kernel 
*                              Copyright(c) 2024 fanxsp
*                         SPDX-License-Identifier: APACHE-2.0
*       This software is subject to an open source license and is distributed by fanxsp,
*       pursuant to the terms of the Apache License.
******************************************************************************************/
/***************************************************************************************** 
* 				TinyRtos51 OSCore.c
* Author: fanxsp 
* Revision: 1.00
* Date: 2023-08-24
* Revision: 1.01
* Date: 2024-03-29
*	优化代码,减小了代码长度
* Revision: 1.10
* Date: 2024-04-07
*	增加reentrant函数支持
* Revision: 1.20
* Date：2024-04-20
*	1. 新增在任务中挂起其它任务(可裁剪)
*   2. 新增动态优先级支持(可裁剪)
*	3. 修正一个链表操作的错误(会造成多个任务等待同一事件时，系统崩溃)
******************************************************************************************/
#define __IN_OSCORE_C
#include "tinyrtos51.h"

OStypeInt8u OSLOC_STACKS OSIdleStack[OSIDLE_STACKS] ; 
void OSIdle(void) ;

/* OS初始化   */
void OSInit(void)  
{
	OStypeInt8u i ;
	
#if OSMETHOD > 0
	OSPendSVInit() ;
#endif

	for(i=OSTASKS ;i<OSTASKS+OSBINSEMS+OSSEMS+OSEFLAGS+OSMSGQS+1; i++)
	{
		OSListArea[i].next = i;
		OSListArea[i].prev = i;
	}
//	OSTcbArea[OSTCBIDLE].state = OSTCB_ELIGIBLE ;
	// 设置空闲任务
	OSTcbArea[OSTCBIDLE].prio = OSLOW_PRIO ;
	OSTcbArea[OSTCBIDLE].stackBottom = OSIdleStack ;
	OSTcbArea[OSTCBIDLE].stackSize = 3 ;
	OSIdleStack[0] = (OStypeInt16u)OSIdle ;
	OSIdleStack[1] = (OStypeInt16u)OSIdle>>8 ;
}

/***********************************************************************************************
* 创建任务
* 参数：
*	tID -任务ID  
*	fp -任务函数指针 
*	stack -任务堆栈指针 
*	prio -任务优先级
*   stackSize -堆栈总长度  largeStackSize -large reentrant堆栈长度 
************************************************************************************************/
#if OSENABLE_REENTRANT_SMALL > 0
void OSCreateTask(
	OStypeInt8u tID,
	OStypeTFP fp,
	OStypeInt8u OSLOC_STACKS* stack,
	OStypeInt8u stackSize,
	OStypeInt8u largeStackSize,
	OStypeInt8u prio) 
{
#elif OSENABLE_REENTRANT_LARGE >0
void OSCreateTask(
	OStypeInt8u tID,
	OStypeTFP fp,
	OStypeInt8u OSLOC_STACKS* stack,
	OStypeInt8u stackSize,
	OStypeInt8u prio) 
{
#else
void OSCreateTask(
	OStypeInt8u tID,
	OStypeTFP fp,
	OStypeInt8u OSLOC_STACKS* stack,
	OStypeInt8u prio) 
{
#endif
#if OSENABLE_ERROR_CHECKING > 0
	if(tID > OSTASKS-1) return ;
#endif
	OSTcbArea[tID].prio = prio ;
//	OSTcbArea[tID].state = OSTCB_ELIGIBLE ;
	OSTcbArea[tID].stackBottom = stack ;
	OSTcbArea[tID].stackSize = 3 ;
#if OSENABLE_REENTRANT_SMALL > 0
	OSTcbArea[tID].smallStackBottom = stack + stackSize - largeStackSize - 1 ;
//	OSTcbArea[tID].smallStackSize = 0 ;
#endif
#if OSENABLE_REENTRANT_LARGE > 0
	OSTcbArea[tID].largeStackTop = stack + stackSize ;
#endif
	stack[0] = (OStypeInt16u)fp ;
	stack[1] = (OStypeInt16u)fp>>8 ;
	OSAppendToList(OSHeadEligible,tID) ;
}

/*********************************************************************************************** 
* TCB按优先级升序添加到就绪表或等待列表
* 参数：listHead 就绪表或等待列表表头指针
*       tID 任务ID
* 从高优先级开始搜索 插入高优级任务的时间是固定的,和任务数无关
***********************************************************************************************/
void OSAppendToList(OStypeInt8u listHead,OStypeInt8u tID) 
{
	OStypeInt8u i ;
	
	i = OSListArea[listHead].prev ;
	while(i != listHead && OSTcbArea[i].prio <= OSTcbArea[tID].prio)
	{
		i = OSListArea[i].prev ;
	}
	OSListArea[tID].prev = i ;
	OSListArea[tID].next = OSListArea[i].next ;
	OSListArea[i].next = tID ;
	OSListArea[OSListArea[tID].next].prev = tID ;
#if OSENABLE_SETPRIO > 0
	OSTcbArea[tID].inList = listHead ;
#endif
}

/************************************************************************************************ 
* 从就绪表或等待列中删除任务 必须确保任务在列表中 
* 参数：tID 任务ID
*************************************************************************************************/
void OSDelFromList(OStypeInt8u tID) 
{
	OSListArea[OSListArea[tID].prev].next = OSListArea[tID].next ;
	OSListArea[OSListArea[tID].next].prev = OSListArea[tID].prev ;
#if OSENABLE_SETPRIO > 0
	OSTcbArea[tID].inList = 0 ;
#endif
}

/* 空闲任务  */
void OSIdle(void)
{
	while(1)
	{
		UserIdle() ;
#if OSMETHOD == 0
		OSEnterCritical() ;
		OSTaskSw() ;
		OSExitCritical() ;
#endif
	}
}

/* 任务级 任务调度  */
void OSSched(void)  
{
	OSEnterCritical() ; 
	OSTaskSw() ;  
	OSExitCritical() ; 
}

/**************************************************************************************************** 
* 在任务中挂起任务 
* 不支持在中断挂起任务，因为在中断中挂起当前任务有可能导致意外的后果。原因分析如下：在中断中挂起当
* 前任务时，会将当前任务的TCB从就绪表中删除，由于是采用模拟pendSV中断来切换任务，程序必须先返回当前
* 任务，然后响应模拟pendSV中断进行任务切换，由于中断响应的时序回题，当前任务有可能先运行一段程序后才
* 响应pendSV中断，如当前任务在这段程序中操作就绪表，就会导致不可预知的意外后果。虽然在中断中挂起其它
* 任务，不会导致这个问题，为了避免意外，不支持在中断挂起任务。                  
*****************************************************************************************************/
#if OSENABLE_SUSPENDID > 0 
void OSTaskSuspendID(OStypeInt8u tID)
{
#if OSENABLE_ERROR_CHECKING > 1
	if(tID > OSTASKS-1) return ;
	if(OSTcbArea[tID].stackSize == 0) return ;
#endif
	OSEnterCritical() ;
	OSTcbArea[tID].delay = 0 ;
	if((OSTcbArea[tID].state == OSTCB_ELIGIBLE)||(OSTcbArea[tID].state == OSTCB_WAITING))
	{
		OSDelFromList(tID) ;
	}
	OSTcbArea[tID].state = OSTCB_SUSPENDED ;
	if(tID == OSTaskRunning)
	{
		OSTaskSw() ;  
	}
	OSExitCritical() ;
}
#else
void OSTaskSuspend(void)  /* 当前任务挂起 只能在任务级调用  */
{
	OSEnterCritical() ;
	OSTcbArea[OSTaskRunning].state = OSTCB_SUSPENDED ;
	OSDelFromList(OSTaskRunning) ;
	OSTaskSw() ;
	OSExitCritical() ;
}
#endif

/*******************************************************************************************************
* 中断中恢复挂起的任务
* 参数：tID: 任务ID
********************************************************************************************************/
void OSIntResume(OStypeInt8u tID) 
{
#if OSENABLE_ERROR_CHECKING > 1
	if(tID > OSTASKS-1) return ;
#endif
	OSEnterCritical() ;
	OSResume(tID) ;
#if OSMETHOD > 0
	OSPendSVAct() ;
#endif
	OSExitCritical() ;
}

/*******************************************************************************************************
* 恢复挂起的任务 不含进/出临界区 不能直接调用
* 参数：tID: 任务ID
********************************************************************************************************/
void OSResume(OStypeInt8u tID) 
{
	if(OSTcbArea[tID].state == OSTCB_SUSPENDED)
	{
		OSTcbArea[tID].state = OSTCB_ELIGIBLE ;
		OSAppendToList(OSHeadEligible,tID) ;
	}	
}

/*******************************************************************************************************
* 任务级恢复挂起的任务
* 参数：tID: 任务ID
********************************************************************************************************/
void OSTaskResume(OStypeInt8u tID) 
{
#if OSENABLE_ERROR_CHECKING > 1
	if(tID > OSTASKS-1) return ;
#endif
	OSEnterCritical() ;
	OSResume(tID) ;
#if OSMETHOD > 0
	OSTaskSw() ;
#endif
	OSExitCritical() ;
}

/* 节拍处理  */
#if OSBYTES_OF_DELAYS > 0
OSCORE_EXT void OSTimeTick() 
{
	OStypeInt8u i ;
	
#if OSTIMER_PRESCALAR > 1
	static OStypeInt8u tickSum ;
	
	if(++tickSum == OSTIMER_PRESCALAR) tickSum = 0 ;
	if( tickSum != 0) return ;
#endif

	OSEnterCritical() ;
	for(i=0;i<OSTASKS;i++)
	{
		if(OSTcbArea[i].delay != 0)
		{
			if(--(OSTcbArea[i].delay) == 0)
			{
#if OSBINSEMS>0 || OSEFLAGS>0 || OSSEMS>0 || OSMSGQS>0
				if(OSTcbArea[i].state == OSTCB_WAITING)
				{
					OSDelFromList(i) ;
					OSTcbArea[i].timeout = 1 ;
				}
#endif
				OSTcbArea[i].state = OSTCB_ELIGIBLE  ;
				OSAppendToList(OSHeadEligible,i) ;
#if OSMETHOD > 0
				OSPendSVAct() ;
#endif
			}
		}
	}
	OSExitCritical() ;
}
#endif

/*******************************************************************************************************
* 任务延时   
* 参数：ticks 延时节拍数                    
********************************************************************************************************/
#if OSBYTES_OF_DELAYS > 0
OSCORE_EXT void OSDelay(OStypeOfDelays ticks) 
{
	if(ticks == 0) return ;
	OSEnterCritical() ;
	OSTcbArea[OSTaskRunning].delay = ticks ;
	OSTcbArea[OSTaskRunning].state = OSTCB_DELAYING ;
	OSDelFromList(OSTaskRunning) ;
	OSTaskSw() ;
	OSExitCritical() ;
}
#endif

/******************************************************************************************************* 
* 超时检测 
* 返回值：1 超时   0 未超时
********************************************************************************************************/
OStypeInt8u OSTimeOut(void) 
{
	return OSTcbArea[OSTaskRunning].timeout ;
}

#if OSENABLE_SETPRIO > 0
/*************************************************************************************************
* 任务中设置指定任务的优先级   
* 参数：tID: 任务ID  prio : 任务优先级                    
*************************************************************************************************/
void OSTaskSetPrio(OStypeInt8u tID,OStypeInt8u prio) 
{
#if OSENABLE_ERROR_CHECKING > 1
	if(tID > OSTASKS-1) return ;
	if(prio > OSLOW_PRIO-1) return ;
#endif
	OSEnterCritical() ;
	OSSetPrio(tID,prio) ;
#if OSMETHOD > 0
	OSTaskSw() ;
#endif
	OSExitCritical() ;
}

/*************************************************************************************************
* 中断中设置指定任务的优先级   
* 参数：tID: 任务ID  prio : 任务优先级                    
*************************************************************************************************/
void OSIntSetPrio(OStypeInt8u tID,OStypeInt8u prio) 
{
#if OSENABLE_ERROR_CHECKING > 1
	if(tID > OSTASKS-1) return ;
	if(prio > OSLOW_PRIO-1) return ;
#endif
	OSEnterCritical() ;
	OSSetPrio(tID,prio) ;
#if OSMETHOD > 0
	OSPendSVAct() ;
#endif
	OSExitCritical() ;
}

/*******************************************************************************************************
* 设置指定任务的优先级 不含进/出临界区 不能直接调用
* 参数：tID: 任务ID  prio : 任务优先级
********************************************************************************************************/
void OSSetPrio(OStypeInt8u tID,OStypeInt8u prio)
{
	OStypeInt8u tmp ;
	
	OSTcbArea[tID].prio = prio ;
	if(OSTcbArea[tID].inList != 0)
	{
		tmp = OSTcbArea[tID].inList ;
		OSDelFromList(tID) ;
		OSAppendToList(tmp,tID) ;		
	}
}
#endif

/*******************************************************************************************************
* 查询任务状态
* 参数：tID: 任务ID 
* 返回值：0(OSTCB_ELIGIBLE)-就绪 1(OSTCB_SUSPENDED)-挂起 2(OSTCB_DELAYING)-延时  3(OSTCB_WAITING)-等待事件   
********************************************************************************************************/
OStypeInt8u OSTaskStateQuery(OStypeInt8u tID)
{
	return OSTcbArea[tID].state ;
}

/*******************************************************************************************************
* 查询任务优先级
* 参数：tID: 任务ID 
* 返回值：任务优先级   
********************************************************************************************************/
OStypeInt8u OSTaskPrioQuery(OStypeInt8u tID)
{
	return OSTcbArea[tID].prio ;
}

/*******************************************************************************************************
*  PendSV 中断
********************************************************************************************************/
#if OSMETHOD > 0
void OSPendSVISR(void) __interrupt OSPendSVIRQ
{
	OSPendSVClr() ;
	OSIntTaskSw() ;
}
#endif
